﻿//*********************************************************

//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Microsoft Public License.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.

//*********************************************************

using System;
using System.Windows;
using System.Windows.Controls;

namespace ODataTestGen.Wizard
{
    using System.ComponentModel;
    using System.Diagnostics;
    using System.IO;
    using System.Threading;
    using System.Windows.Forms;
    using System.Xml.Linq;
    using ODataTestGen.Data;
    using ODataTestGen.OData;

    /// <summary>
    /// Interaction logic for ChooseOutputDirectory.xaml
    /// </summary>
    public partial class ChooseOutputDirectory : Page
    {
        public BackgroundWorker worker;

        public ChooseOutputDirectory()
        {
            InitializeComponent();
            this.progressBarStatus.Visibility = Visibility.Hidden;
            this.progressBarStatus.Orientation = System.Windows.Controls.Orientation.Horizontal;
            this.progressBarStatus.IsIndeterminate = false;
            this.progressBarStatus.Minimum = 0;
            this.progressBarStatus.Maximum = 100;
            this.textBlockLocation.Text = Directory.GetCurrentDirectory();
            this.textBoxProjectName.Text = "ODataTests";
        }


        public TestGenerationConfiguration Configuration { get; set ;}

        private delegate void UpdateTextBlockDelegate(System.Windows.DependencyProperty dp, Object value);

        private static Action EmptyDelegate = delegate() { };

        private void buttonBack_Click(object sender, RoutedEventArgs e)
        {
            this.NavigationService.GoBack();
        }

        public void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            int index = 1;
            string[] args = (string[])e.Argument;
            string outputDirectory = args[0];
            string projectName = args[1];
            worker.ReportProgress(index++, "Generating Dependency Files");
            generateDependencyFiles(outputDirectory, projectName);
                        
            foreach (TestTemplateConfiguration ttc in this.Configuration.Tests)
            {
                worker.ReportProgress(index++, ttc.fullPath);

                string t4File = ttc.fullPath;
                try
                {
                    string relatedPath = ttc.relatedPath;
                    relatedPath = relatedPath.Replace(".tt", "");
                    string dir = Path.Combine(outputDirectory, relatedPath.Substring(0, relatedPath.LastIndexOf("\\")));
                    if (!Directory.Exists(dir))
                        Directory.CreateDirectory(dir);
                    for (int i = 0; i < 10; i++)
                    {
                        if (Directory.Exists(dir))
                            break;
                        Thread.Sleep(200);
                        if (i == 10)
                        {
                            MessageBox.Show("Error: Failed to create output directory: " + dir);
                            return;
                        }
                    }
                    string outputFile = Path.Combine(outputDirectory, relatedPath + ".cs");
                    if (File.Exists(outputFile))
                        File.Delete(outputFile);
                    for (int i = 0; i < 10; i++)
                    {
                        if (!File.Exists(outputFile))
                            break;
                        Thread.Sleep(200);
                    }
                    T4Helpers.RunT4Template(t4File, outputFile, outputDirectory);
                }
                catch (OperationCanceledException ex)
                {
                    MessageBox.Show("Failed to generate test: " + ttc.testName + "\n\n" + ex);
                }
            }    
        }

        public void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            int index = e.ProgressPercentage;
            string path = (string)e.UserState;
            this.textBlockCurrentTest.Text = "Generating Test(" + index + "/" + (this.Configuration.Tests.Count + 1) + "): " + path;
            int currentValue = (int)((double)index * 100 / (this.Configuration.Tests.Count + 1));
            this.progressBarStatus.Value = currentValue;
        }

        public void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.textBlockCurrentTest.Text = "Tests generation is completed.";
            this.buttonCancel.Content = "Close";
            Process.Start(Path.Combine(this.textBlockLocation.Text, this.textBoxProjectName.Text));
        }

        /// <summary>
        /// This function calls T4Helpers.RunT4Template to actually generate the tests based from users choices on entity sets and templates
        /// </summary>
        private void buttonNext_Click(object sender, RoutedEventArgs e)
        {
            //Locate the TextTemplating path and save temporary files
            this.buttonFinish.IsEnabled = false;
            string outputDirectory = Path.Combine(this.textBlockLocation.Text, this.textBoxProjectName.Text);
            try
            {
                if (Directory.Exists(outputDirectory))
                    Directory.Delete(outputDirectory, true);
            }
            catch (IOException ex)
            {
                MessageBox.Show("Error: failed to generate output folder: \n\n" + ex);
                this.buttonFinish.IsEnabled = true;
                return;
            }
            
            for (int i = 0; i < 10; i++)
            {
                if (!Directory.Exists(outputDirectory))
                    break;
                Thread.Sleep(200); 
            }
            Directory.CreateDirectory(outputDirectory);
            for(int i = 0; i < 10; i++)
            {
                if (Directory.Exists(outputDirectory))
                    break;
                Thread.Sleep(200);
                if (i == 10)
                {
                    MessageBox.Show("Error: Failed to create output directory.");
                    this.buttonFinish.IsEnabled = true;
                    return;
                }

            }

            Directory.CreateDirectory(Path.Combine(outputDirectory, "ODataVerificationMethods"));
            string programFilesDir = Environment.GetEnvironmentVariable("ProgramFiles(x86)");
            if (programFilesDir == null)
                programFilesDir = Environment.GetEnvironmentVariable("ProgramFiles");

            string ttToolDir = Path.Combine(programFilesDir, @"Common Files\microsoft shared\TextTemplating\");
            string[] textTransformExes = Directory.GetFiles(ttToolDir, "TextTransform.exe", SearchOption.AllDirectories);
            if (textTransformExes.Length == 0)
            {
                MessageBox.Show("Error: there is no TextTemplating installed under path \"" + ttToolDir + "\".");
                return;
            }

            string path = Path.Combine(outputDirectory, this.textBoxProjectName.Text + ".admx");
            saveTempCfgFile(path);
            path = Path.Combine(outputDirectory, this.textBoxProjectName.Text + ".edmx");
            this.Configuration.edmxElement.Save(path);

            this.progressBarStatus.Visibility = Visibility.Visible;
            this.textBlockCurrentTest.Text = "Generating Dependency Files";

            worker = new BackgroundWorker();
            worker.WorkerReportsProgress = true;
            worker.WorkerSupportsCancellation = true;
            worker.DoWork += bw_DoWork;
            worker.ProgressChanged += bw_ProgressChanged;
            worker.RunWorkerCompleted += bw_RunWorkerCompleted;

            string[] outputNames = { outputDirectory, this.textBoxProjectName.Text };

            worker.RunWorkerAsync(outputNames);

        }

        private void buttonBrowse_Click(object sender, RoutedEventArgs e)
        {
            System.Windows.Forms.FolderBrowserDialog dlg = new System.Windows.Forms.FolderBrowserDialog();
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                this.textBlockLocation.Text = dlg.SelectedPath;
            }            
        }

        /// <summary>
        /// This function saves the current configuration(both the selected entity sets and tests) to a temporary file.
        /// Parameter: path - The path where the configuration is stored.
        /// </summary>
        private void saveTempCfgFile(string path)
        {
            
            if (File.Exists(path))
                File.Delete(path);

            XElement root = new XElement(ODataConstants.EdmxNamespace + "AnnotatedEdmx");
            XElement entitySets = new XElement(ODataConstants.EdmxNamespace + "EntityConfigurations");
            XElement selectedTest = new XElement(ODataConstants.EdmxNamespace + "SelectedTests");
            XElement defaultServiceUri = new XElement(ODataConstants.EdmxNamespace + "DefaultServiceUri");
            XElement projectName = new XElement(ODataConstants.EdmxNamespace + "ProjectName");
            projectName.Value = this.textBoxProjectName.Text;
            defaultServiceUri.Value = this.Configuration.defaultServiceRoot;
            root.Add(defaultServiceUri);
            root.Add(projectName);
            root.Add(entitySets);
            root.Add(selectedTest);

            foreach (EntitySetConfiguration cfg in this.Configuration.Entities)
            {
                XElement entitySet = new XElement(ODataConstants.EdmxNamespace + "EntitySet");
                entitySet.SetAttributeValue("Name", cfg.Name);
                entitySet.SetAttributeValue("IsEnabled", cfg.IsEnabled);
                entitySet.SetAttributeValue("GetTest", cfg.GetTest);
                entitySet.SetAttributeValue("PostTest", cfg.PostTest);
                entitySet.SetAttributeValue("PutTest", cfg.PutTest);
                entitySet.SetAttributeValue("DeleteTest", cfg.DeleteTest);
                entitySets.Add(entitySet);
            }

            foreach (TestTemplateConfiguration tt in this.Configuration.Tests)
            {
                XElement test = new XElement(ODataConstants.EdmxNamespace + "Test");
                test.SetValue(tt.testName);
                test.SetAttributeValue("Path", tt.fullPath);
                test.SetAttributeValue("RelatedPath", tt.relatedPath);
                selectedTest.Add(test);
            }
            root.Save(path);    
            
        }

        private void buttonCancel_Click(object sender, RoutedEventArgs e)
        {
            ((Window)this.Parent).Close();
        }

        private void textBoxProjectName_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (this.textBoxProjectName.Text.Length == 0)
                this.buttonFinish.IsEnabled = false;
            else
                this.buttonFinish.IsEnabled = true;
        }

        public void generateDependencyFiles(string outputDirectory, string projectName)
        {
            try
            {
                string dependencyFolder = Path.Combine(this.Configuration.startingDirectory, @"TestTemplates\Dependency Templates");
                string t4File = Path.Combine(dependencyFolder, "project.tt");
                string outputFile = Path.Combine(outputDirectory, projectName + ".csproj");
                T4Helpers.RunT4Template(t4File, outputFile, outputDirectory);
                t4File = Path.Combine(dependencyFolder, "UriAnalyzer.tt");
                outputFile = Path.Combine(Path.Combine(outputDirectory, @"ODataVerificationMethods"), "UriAnalyzer.cs");
                T4Helpers.RunT4Template(t4File, outputFile, outputDirectory);
                t4File = Path.Combine(dependencyFolder, "PayloadAnalyzer.tt");
                outputFile = Path.Combine(Path.Combine(outputDirectory, @"ODataVerificationMethods"), "PayloadAnalyzer.cs");
                T4Helpers.RunT4Template(t4File, outputFile, outputDirectory);
            }
            catch (OperationCanceledException ex)
            {
                MessageBox.Show("Failed to generate dependency files.\n\n" + ex);
            }  
        }

    }
}
